<?php

declare(strict_types=1);
/**
 * This file is part of habo_new_media.
 *
 * @link     https://www.berlen.cn/
 * @contact  tech@berlen.cn
 * @license  http://gitlab.rehou.cn/habo/habo_new_media/-/blob/master/README.md
 */
namespace app\service\report\ocean;

use app\enum\CommonEnum;
use app\enum\OceanEnum;
use app\enum\OeEnum;
use app\enum\ReportRecordEnum;
use app\model\ClientAccountModel;
use app\model\ReportRecordPushOeAccountDateModel;
use app\model\ReportRecordPushOeAccountModel;
use app\service\BaseService;
use app\service\client\AccountService;
use app\service\ocean\AdvertiserService;
use app\service\report\ReportDataService;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\Model;

final class OceanService extends BaseService
{
    /**
     * @var int 拉取日期数
     */
    protected const MAX_STEP = 3;

    /**
     * @var null|ReportDataService 数据拉取服务
     */
    protected ?ReportDataService $reportDataService;

    /**
     * @var null|array|ClientAccountModel|mixed|Model 账户模型
     */
    protected ?ClientAccountModel $accountObj;

    public function __construct(ReportDataService $service)
    {
        $this->reportDataService = $service;
    }

    /**
     * 设置媒体数据.
     *
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getMediaData(): bool
    {
        $dimension = $this->reportDataService->getReportAttr('dimension');
        $advertiserIds = json_decode($this->reportDataService->getReportAttr('adv_unit_plan_lists'), true);
        foreach ($advertiserIds as $advertiserId) {
            $this->accountObj = AccountService::getAccountModel(0, $advertiserId);
            if (! $this->accountObj instanceof ClientAccountModel || ! $this->accountObj->getKey()) {
                return self::setError('账户数据不存在');
            }

            // 广告账户
            if ($dimension == ReportRecordEnum::REPORT_DIMENSION_ACCOUNT) {
                if (! $this->accountData(strval($advertiserId))) {
                    return false;
                }
            }
            // 广告计划
            if ($dimension == ReportRecordEnum::REPORT_DIMENSION_CAMPAIGN) {
                return self::setError('不支持广告计划拉取');
            }
            // 广告组
            if ($dimension == ReportRecordEnum::REPORT_DIMENSION_UNIT) {
                return self::setError('不支持广告组拉取');
            }

        }
        return true;
    }

    /**
     * 账户数据保存.
     *
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function accountData(string $advertiserId = ''): bool
    {
        $pullDate = strtotime(strval($this->reportDataService->getPullAttr('date')));
        // 日数据
        $statDate = date('Y-m-d', strtotime('-2 day'));
        $res = $this->getAccountDailyData($advertiserId, 1, $statDate);
        if ($res === false) {
            return false;
        }
        if (count($res['data']['list']) == 0) {
            return true;
        }
        foreach ($res['data']['list'] as $item) {
            $statDate = date('Ymd', strtotime($item['stat_datetime']));
            if (! $this->setAccountDailyData($advertiserId, $item, $statDate)) {
                return false;
            }
        }

        // 小时数据
        for ($step = 0; $step < self::MAX_STEP; ++$step) {
            $statDate = date('Y-m-d', $pullDate - ($step * 24 * 3600));
            $res = $this->getAccountHourlyData($advertiserId, 1, $statDate);
            if ($res === false) {
                return false;
            }
            if (count($res['data']['list']) == 0) {
                break;
            }
            foreach ($res['data']['list'] as $item) {
                $statDate = date('Y-m-d', strtotime($item['stat_datetime']));
                $statHour = intval(date('H', strtotime($item['stat_datetime'])));
                if (! $this->setAccountHourlyData($advertiserId, $item, $statDate, $statHour)) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 获取账户媒体日数据.
     *
     * @param string $advertiserId 账户id
     * @param int $page 分页
     * @param string $statDate 拉取日志
     * @return bool|mixed|string
     */
    private function getAccountDailyData(string $advertiserId = '', int $page = 1, string $statDate = '')
    {
        $token = $this->getToken($advertiserId);
        if (! $token) {
            return self::setError('token获取失败');
        }
        $headers = [
            'Access-Token' => $token,
            'Content-Type' => 'application/json',
        ];
        $statDate = $statDate ?: date('Y-m-d', strtotime(strval($this->reportDataService->getPullAttr('date'))));
        $params = [
            'advertiser_id' => $advertiserId,
            'start_date' => $statDate,
            'end_date' => date('Y-m-d', strtotime(strval($this->reportDataService->getPullAttr('date')))),
            'time_granularity' => OceanEnum::STAT_TIME_GRANULARITY_DAILY,
            'fields' => OceanEnum::REPORT_DEFAULT_FIELDS_OE,
            'page' => $page,
            'page_size' => OeEnum::MAX_PAGE_SIZE,
        ];
        return AdvertiserService::getAccount($headers, $params);
    }

    /**
     * 设置账户数据日数据.
     *
     * @param string $advertiserId 账户id
     * @param array $content 数据内容
     * @param string $statDate 拉取日志
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function setAccountDailyData(string $advertiserId, array $content, string $statDate = ''): bool
    {
        $model = $this->getAccountDailyModel($advertiserId, $statDate);
        $model->setAttrs([
            'advertiser_id' => $advertiserId,
            'account_id' => $this->accountObj->getKey(),
            'stat_date' => date('Ymd', strtotime($statDate)),
            'stat_hour' => $this->reportDataService->getPullAttr('hour'),
            'dimension' => ReportRecordEnum::REPORT_DIMENSION_ACCOUNT,
            'data_content' => json_encode($content),
            'date_type' => $this->reportDataService->getReportAttr('report_date_type'),
        ]);
        if (! $model->save()) {
            return self::setError('保存账户日数据失败');
        }
        return true;
    }

    /**
     * 获取账户模型.
     *
     * @param string $advertiserId 账户id
     * @param string $statDate 拉取日志
     * @return null|array|mixed|Model|ReportRecordPushOeAccountDateModel
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function getAccountDailyModel(string $advertiserId, string $statDate = '')
    {
        $statDate = $statDate ?: date('Y-m-d', strtotime(strval($this->reportDataService->getPullAttr('date'))));
        $where = [
            ['advertiser_id', '=', $advertiserId],
            ['stat_date', '=', date('Ymd', strtotime($statDate))],
            ['stat_hour', '=', $this->reportDataService->getPullAttr('hour')],
            ['date_type', '=', $this->reportDataService->getReportAttr('report_date_type')],
        ];
        $model = (new ReportRecordPushOeAccountDateModel())->where($where)->field(true)->find();
        if (! $model instanceof ReportRecordPushOeAccountDateModel || ! $model->getKey()) {
            $model = new ReportRecordPushOeAccountDateModel();
        }
        return $model;
    }

    /**
     * 获取账户媒体小时数据.
     *
     * @param string $advertiserId 账户id
     * @param int $page 分页
     * @param string $statDate 拉取日志
     * @return bool|mixed|string
     */
    private function getAccountHourlyData(string $advertiserId = '', int $page = 1, string $statDate = '')
    {
        usleep(rand(200000, 500000));
        $token = $this->getToken($advertiserId);
        if (! $token) {
            return self::setError('token获取失败');
        }
        $headers = [
            'Access-Token' => $token,
            'Content-Type' => 'application/json',
        ];
        $statDate = $statDate ?: date('Y-m-d', strtotime(strval($this->reportDataService->getPullAttr('date'))));
        $params = [
            'advertiser_id' => $advertiserId,
            'start_date' => $statDate,
            'end_date' => $statDate,
            'time_granularity' => OceanEnum::STAT_TIME_GRANULARITY_HOURLY,
            'fields' => OceanEnum::REPORT_DEFAULT_FIELDS_OE,
            'page' => $page,
            'page_size' => OeEnum::MAX_PAGE_SIZE,
        ];
        return AdvertiserService::getAccount($headers, $params);
    }

    /**
     * 设置账户数据小时数据.
     *
     * @param string $advertiserId 账户id
     * @param array $content 数据内容
     * @param string $statDate 拉取日志
     * @param int $statHour 拉取小时
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function setAccountHourlyData(string $advertiserId, array $content, string $statDate = '', int $statHour = 0): bool
    {
        $statDate = $statDate ?: date('Y-m-d', strtotime(strval($this->reportDataService->getPullAttr('date'))));
        $model = $this->getAccountHourlyModel($advertiserId, $statDate, $statHour);
        $model->setAttrs([
            'advertiser_id' => $advertiserId,
            'account_id' => $this->accountObj->getKey(),
            'stat_date' => date('Ymd', strtotime($statDate)),
            'stat_hour' => $statHour,
            'dimension' => ReportRecordEnum::REPORT_DIMENSION_ACCOUNT,
            'data_content' => json_encode($content),
            'date_type' => $this->reportDataService->getReportAttr('report_date_type'),
        ]);
        if (! $model->save()) {
            return self::setError('保存账户日数据失败');
        }
        return true;
    }

    /**
     * 获取账户模型.
     *
     * @param string $advertiserId 账户id
     * @param string $statDate 拉取日期
     * @param int $statHour 拉取小时
     * @return null|array|mixed|Model|ReportRecordPushOeAccountModel
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private function getAccountHourlyModel(string $advertiserId, string $statDate = '', int $statHour = 0)
    {
        $statDate = $statDate ?: date('Y-m-d', strtotime(strval($this->reportDataService->getPullAttr('date'))));
        $where = [
            ['advertiser_id', '=', $advertiserId],
            ['stat_date', '=', date('Ymd', strtotime($statDate))],
            ['stat_hour', '=', $statHour],
            ['date_type', '=', $this->reportDataService->getReportAttr('report_date_type')],
        ];
        $model = (new ReportRecordPushOeAccountModel())->where($where)->field(true)->find();
        if (! $model instanceof ReportRecordPushOeAccountModel || ! $model->getKey()) {
            $model = new ReportRecordPushOeAccountModel();
        }
        return $model;
    }

    /**
     * 获取token.
     *
     * @param string $advertiserId 账户id
     */
    private function getToken(string $advertiserId = ''): string
    {
        return AccountService::getToken(CommonEnum::OE_MEDIA, $advertiserId);
    }
}
